/**
 * @file slave.c
 * @brief 从站的通信动作
 * @details 
 *          这个文件记录了从站在通信过程中所需要的行为,如
 *          1. 接收主站请求 @see
 *          2. 对主站的请求进行应答 @see
 *          3.
 *          处理主站发出来的请求,这个应该会做成一个
 *          钩子
 *          @see 
 * @author fulong 
 * @data 7/4/2014
 * @version 1.0
 * @par Copyright (c):
 * @par First build
 *     version: fulong 
 *     data: 7/4/2014
 */
#include "slave.h"
#include "slave_cmd.h"
#include "../../../hal/hal_uart.h"
#include "common.h"
#ifdef MODBUS_ASCII_MODE
#include "../ascii/ascii.h"
#endif
#ifdef MODBUS_RTU_MODE
#include "../rtu/rtu.h"
#endif
#include "../../../dev/uart.h"

#ifdef MODBUS_ENABLE

#define RECEIVE_PROCESS_MAX_LEN 40

#define slave_unlock() slave_receiving_data=0
#define slave_lock() slave_receiving_data=1

#define slave_process_message_exist() slave_mb_status.data_process_done==1


static mb_adu_struct slave_receive_adu;
mb_status_struct slave_mb_status;
receive_frame_struct slave_receive_frame;

/*!< 数据接收缓冲区 */
uint8 slave_process_data[ RECEIVE_PROCESS_MAX_LEN ];
/*!< 响应数据缓冲区 这个缓冲区只有当adu被处理好了才会处理这个缓冲区,并且它的值只在 @see slave_modbus_process 中修改 */
uint8 mb_rsp_pdu_buffer[ RSP_PUD_MAX_LEN ];
/*!< 当接收到数据的时候,这个变量会锁住,来接收主站发送的数据 */
uint8 slave_receiving_counter;

void slave_verify_error(void);
static uint8 slave_modbus_process(mb_adu_struct *mb_adu, uint8 *buffer);

/*! \brief 从站的初始化配置,绑定某些数据,初始化该传输模式下的条件.
 * \param
 * \retrun
 */
void slave_configure(void) {
    slave_receiving_counter = 0;
    slave_receive_adu.mb_req_pdu.request_data = slave_process_data;
    slave_mb_status.mb_adu = (mb_adu_struct *)&slave_receive_adu;
    modbus_operation.modbus_init();
}
/*! \brief 从站的接收处理函数
 * \param
 * \retrun
 */
void slave_receive(void) {
    uint8 i;
    if (slave_receive_frame.receive_frame_done == 1 && slave_mb_status.mb_status == MODBUS_FREE) {
#ifdef __DEBUG__
        debug_uart_send_string("frame receive done.\r\n");
#endif
        if (slave_receive_frame.frame_len < FRAME_LEST_LEN || slave_receive_frame.frame_len > FRAME_MAX_LEN) {
            slave_receive_frame.receive_frame_done = 0;
            modbus_operation.modbus_init();
#ifdef __DEBUG__
            debug_uart_send_string("frame  error.\r\n");
#endif
        } else {
#ifdef __DEBUG__
            debug_uart_send_string("frame transform start.\r\n");
#endif
            slave_mb_status.mb_status = MODBUS_BUSY;
            slave_mb_status.mb_frame_len = slave_receive_frame.frame_len;
            modbus_operation.modbus_receive();
            slave_mb_status.mb_status = MODBUS_CHECK_VERIFY;
#ifdef __DEBUG__
            debug_uart_send_string("frame transform done.\r\n");
#endif
        }
    }
}
/*! \brief 从站进行处理接收分类好的数据
 * \param
 * \retrun
 */
void slave_process(void) {
    uint8 verify, data_len;
    mb_rsp_pdu_struct mb_rsp_pdu;
    mb_excep_rsp_pdu_struct mb_excep_rsp_pdu;
    if (slave_process_message_exist()) {
        switch (slave_mb_status.mb_status) {
            /*数据验证状态*/
            case MODBUS_CHECK_VERIFY:
#ifdef __DEBUG__
                debug_uart_send_string("verify...\r\n");
#endif
                if (modbus_operation.adjust_verify(slave_mb_status.mb_adu)) {
                    slave_mb_status.mb_status = MODBUS_VERIFY_ERROR;
                } else {
                    slave_mb_status.mb_status = MODBUS_SELECT_DEVICE;
                }
                /*如果正常进入验证步骤的话,验证完后,就会释放接收帧结构,释放接收帧结构的地方有两个,一个是这里,一个在接收不符合处理最低要求的帧的地方 */
                slave_receive_frame.receive_frame_done = 0;
                break;
                /*进行寻址状态*/
            case MODBUS_SELECT_DEVICE:
#ifdef __DEBUG__
                debug_uart_send_string("addressing...\r\n");
#endif
                if (SLAVE_ADDRESS == slave_mb_status.mb_adu->addr) {
                    slave_mb_status.mb_status = MODBUS_SINGLE_PROCESS;
                } else if (0 == slave_mb_status.mb_adu->addr) {
#ifdef __DEBUG__
                    debug_uart_send_string("boardcast.\r\n");
#endif
                    slave_mb_status.mb_status = MODBUS_BROADCAST_PROCESS;
                } else {
                    slave_mb_status.mb_status = MODBUS_VERIFY_ERROR;
                }
                break;
                /*根据寻址的特性,进行处理,广播处理是不能回复的*/
            case MODBUS_SINGLE_PROCESS:
            case MODBUS_BROADCAST_PROCESS:
#ifdef __DEBUG__
                debug_uart_send_string("get the processed code.\r\n");
#endif
                slave_mb_status.mb_exception_code = slave_modbus_process(slave_mb_status.mb_adu, mb_rsp_pdu_buffer);
                if (slave_mb_status.mb_exception_code == MODBUS_NONE_EXCEPTION) {
                    slave_mb_status.mb_status = MODBUS_PROCESS_SUCCESS;
                } else {
                    slave_mb_status.mb_status = MODBUS_PROCESS_EXCEPTION;
                }
                break;
                /*校验或者寻址错误,进入校验错误状态*/
            case MODBUS_VERIFY_ERROR:
                slave_verify_error();
                modbus_operation.modbus_init();
                break;
                /*处理成功状态,这个状态会回复主机*/
            case MODBUS_PROCESS_SUCCESS:
                if (slave_mb_status.mb_adu->addr != 0) {
#ifdef __DEBUG__
                    debug_uart_send_string("normal respond.\r\n");
#endif
                    mb_rsp_pdu.function_code = slave_mb_status.mb_adu->mb_req_pdu.function_code;
                    mb_rsp_pdu.request_data_len = mb_rsp_pdu_buffer[0];
                    mb_rsp_pdu.response_data = (uint8 *)&mb_rsp_pdu_buffer[1];
                    /*回复主机*/
                    modbus_operation.normal_rsp(&mb_rsp_pdu);
                } else {
#ifdef __DEBUG__
                    debug_uart_send_string("no respond.\r\n");
#endif
                }
                slave_mb_status.mb_status = MODBUS_PROCESS_DONE;
                break;
                /*处理失败状态,这个状态也会回复主机*/
            case MODBUS_PROCESS_EXCEPTION:
                if (slave_mb_status.mb_adu->addr != 0) {
#ifdef __DEBUG__
                    debug_uart_send_string("exception respond.\r\n");
#endif
                    mb_excep_rsp_pdu.exception_code = slave_mb_status.mb_exception_code;
                    mb_excep_rsp_pdu.exception_function_code = slave_mb_status.mb_adu->mb_req_pdu.function_code + 0x80;
                    /*回复主机*/
                    modbus_operation.except_rsp(&mb_excep_rsp_pdu);
                } else {
#ifdef __DEBUG__
                    debug_uart_send_string("no respond.\r\n");
#endif
                }
                slave_mb_status.mb_status = MODBUS_PROCESS_DONE;
                break;
            case MODBUS_PROCESS_DONE:
                if (get_rsp_status()) {
                    /*将modbus恢复到初始状态,等待下一帧的来临*/
                    modbus_operation.modbus_init();
                    reset_rsp_status();
                }
                break;
            default:
                break;
        }
    } else {
    }
}

/*! \brief 从机验证错误后，在验证错误状态下用户处理函数，如果需要处理的话，就在这个函数写处理方法。
 * \param
 * \retrun
 */
void slave_verify_error(void) {
#ifdef __DEBUG__
    debug_uart_send_string("verify error.\r\n");
#endif
}

/*! \brief 处理接收到的功能码
 * \param buffer 这个是从站回应缓冲区,缓冲区的第一位是回应数据的长度,第二位开始就是回应的数据.
 * \param mb_adu 要处理的帧结构
 * \retrun 返回处理结果 @see except_code
 */
static uint8 slave_modbus_process(mb_adu_struct *mb_adu, uint8 *buffer) {
    uint8 exception_code;
    switch (mb_adu->mb_req_pdu.function_code) {
        case READ_COIL_STATUS:
            exception_code = cmd_read_coil_status(mb_adu->mb_req_pdu.request_data, buffer);
            break;
        case READ_INPUT_STATUS:
            exception_code = cmd_read_input_status(mb_adu->mb_req_pdu.request_data, buffer);
            break;
        case READ_HOLDING_REGS:
            exception_code = cmd_read_holding_regs(mb_adu->mb_req_pdu.request_data, buffer);
            break;
        case READ_INPUT_REGS:
            exception_code = cmd_read_input_regs(mb_adu->mb_req_pdu.request_data, buffer);
            break;
        case FORCE_SINGLE_COIL:
            exception_code = cmd_force_single_coil(mb_adu->mb_req_pdu.request_data, buffer);
            break;
        case PRESET_SINGLE_REG:
            exception_code = cmd_preset_single_reg(mb_adu->mb_req_pdu.request_data, buffer);
            break;
        case FORCE_MULTIPLE_REGS:
            exception_code = cmd_force_multiple_regs(mb_adu->mb_req_pdu.request_data, buffer);
            break;
        default:
            exception_code = INVAILD_FUNCTION;
            break;
    }
    return (exception_code);
}
#endif
